#!/usr/bin/env python
# coding=utf-8
# CopyRight(c) 2019 Inspur Electronic Information Industry Co.,Ltd

"""
    secchecksuite/check.py
    check.py for secchecksuite include some check utils
    :CopyRight(c) 2019 Inspur Electronic Information Industry Co.,Ltd
"""

import os
import re
import platform
import signal
import sqlitedb
import constant as const
import seccheck_log
import aes_cbc


def check_plugin_level(plugin_level):
    """
    check plugin_level info
    Args:
        plugin_level:plugin level
    Returns:
        None
    Raises:
        KeyError: value is wrong
    """
    if not plugin_level or plugin_level.upper() not in const.LEVEL:
        raise KeyError('level illegal:{}.may be (A/B/C)'.format(plugin_level))


def check_source(source):
    """
    check source info
    Args:
        source:plugin source
    Returns:
        None
    Raises:
        KeyError: value is wrong
    """
    if not source or source not in const.SOURCE:
        seccheck_log.sec_check_logger.error('source illegal:{}.may be (selfdevelop/thirddevelop)'.format(source))
        raise KeyError('source illegal:{}.may be (selfdevelop/thirddevelop)'.format(source))


def check_export_type(export_type):
    """
    check source info
    Args:
        export_type:plugin level
    Returns:
        None
    Raises:
        KeyError: exporttype  value is wrong
    """
    if not export_type or export_type.upper() not in const.EXPORT_TYPE:
        raise KeyError('exporttype illegal:{}.may be (xml/json)'.format(export_type))


def check_plugin_type(plugin_type):
    """
    check plugin type info
    Args:
        plugin_type:plugin level
    Returns:
        None
    Raises:
        json.decoder.JSONDecodeError:
        KeyError: plugintype illegal not script/exe
    """
    if not plugin_type or plugin_type.upper() not in const.PLUGIN_TYPE:
        seccheck_log.sec_check_logger.error(
            'plugintype illegal.plugintype may be (script/exe) not \'{}\''.format(plugin_type))
        raise KeyError('plugintype illegal.plugintype may be (script/exe) not \'{}\''.format(plugin_type))


def check_file_exist(plugin_exe):
    """
    check configure plugin_exe is valid.
    Args:
        plugin_exe: the configure plugin_exe
    Returns:
        None
    """
    if not plugin_exe or plugin_exe == '':
        raise KeyError('{} is not None'.format(plugin_exe))
    if not os.path.exists(plugin_exe):
        raise KeyError('file {} is not exist'.format(plugin_exe))


def check_info_none(key, value):
    """check info, if None or empty
    Args:
        key: plugin info's key, such as plugin_exe,plugin_config
        value:plugin info's value, such as script, selfdevelop
    Returns:
        plugin_type: script or exe
    Raises:
        BaseException:  An error occurred cleanup a table
    """

    if value is None or value == '':
        raise KeyError('{} is None or empty.'.format(key))


def check_database_valid(config_info, database_name):
    """
    check database is exist
    Args:
        config_info: config info
        database_name:sqlite database name
    Returns:
        True: database is exist
        False: database is not exist
    """
    path = sqlitedb.get_database_path(config_info, database_name)
    try:
        if os.path.exists(path):
            return True
        return False
    except BaseException as err:
        seccheck_log.sec_check_logger.error('remove database {} occur some errors: {}'.format(path, err))
        raise ('remove database {} occur some errors: {}'.format(path, err))


def validate_input_key(input_key):
    """
    validate input_key is valid
    Args:
        input_key: the input_key to be validate
    Returns:
        True/False: input_key is or not valid
    """
    if not input_key:
        return False
    regex = r'[\da-zA-Z~`!@#$%^&*()_+\-={[}\]:;"\'|\\<,>.?/]{8,16}$'
    pattern = re.compile(regex)
    return pattern.match(input_key)


def check_hash_value(plugin, key):
    """
    check plugin_exe's hashvalue and hashvalue in database
    Args:
        plugin: plugin infos include plugin_exe and his hasvalue stored in database
        key: key seed for generating secret key
    Returns:
        code: True:equal / False: not equal
    Raises:
        BaseException:  An error occurred cleanup a table
    """
    try:
        check_file_exist(plugin['plugin_exe'])
        plain_hash = aes_cbc.aes_decrypt(key, plugin['hashvalue'], out=False)
        res = os.popen('sha256sum %s' % plugin['plugin_exe'])
        new_hash_value = res.read()
        if new_hash_value != plain_hash:
            seccheck_log.sec_check_logger.info(
                'plugin:{}\'s hashvalue is not equal(database and local).not execute!'.format(plugin['plugin_exe']))
            raise KeyError(
                'plugin:{}\'s hashvalue is not equal(database and local.not execute!)'.format(plugin['plugin_exe']))
    except BaseException as err:
        seccheck_log.sec_check_logger.error('check_hashvalue occur error:{}'.format(err))
        raise err
    seccheck_log.sec_check_logger.info('plugin_exe:{} hashvavlue:{}'.format(plugin['plugin_exe'], new_hash_value))


def check_level(plugin, config_info):
    """
    check plugin_exe's level and level in config
    Args:
        plugin: plugin infos include plugin_exe and his hasvalue stored in database
        config_info: config information
    Returns:
        code: True:equal / False: not equal
    Raises:
        BaseException:  An error occurred cleanup a table
    """
    try:
        exec_level = config_info.get('pluginexeclevel')
        database_level = plugin['plugin_level']
        check_plugin_level(exec_level)
        check_plugin_level(database_level)
        # level is 'A/B/C',so if pluginexeclevel in config is B, execute B/C level plugins
        if database_level < exec_level:
            seccheck_log.sec_check_logger.info(
                '{} plugin level is lower than plugin exec level.not execute!'.format(plugin['plugin_exe']))
            raise KeyError(
                '{} plugin level is lower than plugin exec level.not execute!'.format(plugin['plugin_exe']))
    except BaseException as err:
        seccheck_log.sec_check_logger.error('check_level occur error:{}'.format(err))
        raise err
    seccheck_log.sec_check_logger.info('plugin_exe:{} database_level:{}'.format(plugin['plugin_exe'], database_level))


def check_stop_flag(plugin, result):
    """
    check plugin_exe's stop flag,if current plugin exists stop flag and result code is not '0', secchecksuite exit.
    Args:
        plugin: plugin infos include plugin_exe and his stop flag stored in database
        result: plugin exec result
    Returns:
        code: 0:break, 1:continue
    Raises:
        BaseException:  An error occurred cleanup a table
    """
    try:
        stop_flag = int(plugin.get('stop_flag', 0))
        result_code = result.get('code', 0)
        seccheck_log.sec_check_logger.info('stop_flag:{} result_code:{}'.format(stop_flag, result_code))
        if stop_flag > 0 and result_code != 0:
            seccheck_log.sec_check_logger.info('plugin {}\'s result is not 0.stop!'.format(plugin.get('plugin_exe')))
            return 0
    except KeyError as e:
        seccheck_log.sec_check_logger.error('check_stop_flag occur error:{}'.format(e))
    return 1


def check_config_info(config_info):
    """
    check config info,such as pluginexelevel, exporttype
    Args:
        config_info: plugins config info
    Raises:
        KeyError
    """
    try:
        check_plugin_level(config_info.get('pluginexeclevel', None))
        check_export_type(config_info.get('exporttype', None))
    except KeyError as e:
        raise e


def check_signal():
    """
    signal process
    Returns:
        None
    Raises:
        signal.ItimerError:
    """
    try:
        if 'Windows' == platform.system():
            pass
        elif 'Linux' == platform.system():
            signal.signal(signal.SIGQUIT, signal.SIG_IGN)
            # signal.signal(signal.SIGCHLD, signal.SIG_IGN)
        else:
            pass
    except signal.ItimerError as e:
        print('signal_check some ItimerError errors occur:{}'.format(e))


def check_ip(value):
    regex = r'[0-9.]+$'
    pattern = re.compile(regex)
    if not pattern.match(value):
        return False
    value_list = value.split('.')
    if len(value_list) != 4:
        return False
    for item in value_list:
        if not 0 <= int(item) <= 255:
            return False
    return True


def check_not_none_dict(value):
    if not value or not isinstance(value, dict):
        return False
    return True


def check_normal_exit(error):
    return isinstance(error, SystemExit) and str(error) == '0'
